package org.windowkit.toolkit.widget;

import java.awt.GridBagConstraints;
import java.awt.GridBagLayout;
import java.awt.GridLayout;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.util.ArrayList;
import java.util.Enumeration;

import javax.swing.AbstractButton;
import javax.swing.ButtonGroup;
import javax.swing.ButtonModel;
import javax.swing.JComponent;
import javax.swing.JPanel;
import javax.swing.JRadioButton;
import javax.swing.event.ChangeEvent;
import javax.swing.event.ChangeListener;


/**
 * A group of radio buttons.
 * @author Eric Lundin
 */
public class RadioButtonPane extends JComponent {
	private static final long serialVersionUID = 1670524046946749804L;

	/** The button group. */
	protected ButtonGroup buttonGroup;

	/** The panel to add the buttons to */
	protected JPanel buttonPanel;

	/** Constraints for the layout of the buttons */
	protected GridBagConstraints gbc;

	/**
	 *
	 */
	public RadioButtonPane() {
		super();
		init();
	}

	private void init() {
		GridBagConstraints buttonPanelConstraints = new GridBagConstraints();
		gbc = new GridBagConstraints();

		buttonGroup = new ButtonGroup();
		buttonPanel = new JPanel(new GridLayout());
		setLayout(new GridBagLayout());
		gbc.anchor = GridBagConstraints.CENTER;
		buttonPanelConstraints.fill = gbc.fill = GridBagConstraints.BOTH;
		buttonPanelConstraints.weightx = gbc.weightx = 1.0;
		buttonPanelConstraints.weighty = gbc.weighty = 1.0;
		add(buttonPanel, gbc);
	}

	/**
	 * Sets the number of rows the buttons should divide up into. Example:<BR> * * *<BR> * * *
	 * <BR> * * * <BR>
	 * -Would be 3 rows.<BR> * * * *<BR> * * * *<BR>
	 * -Would be 2 rows.
	 *
	 * @param rows
	 *            The number of rows this pane should have.
	 */
	public void setRows(final int rows) {
		((GridLayout) buttonPanel.getLayout()).setRows(rows);
	}

	/**
	 * Add a button to the group at the specified position. The position must be
	 * within the bounds of the number of buttons to simply append this button
	 * to the end of the pane call addButton(RadioButton) or this method with
	 * a position of -1.<BR>
	 * <B>Warning:</B> The developer is responsible for setting an identifier
	 * on all buttons passed in through the addButton method after
	 * identifySComponents has been called.
	 *
	 * @param button
	 *            The button to add to the group.
	 */
	public void addButton(final RadioButton button, final int position) {
		buttonPanel.add(button, gbc, position);
		buttonGroup.add(button);
		if (buttonPanel.getParent() != null
				&& buttonPanel.getParent().isShowing()) {
			buttonPanel.getParent().validate();
		}
	}

	/**
	 * Create and add a new radio button. <BR>
	 * <B>Warning:</B> The developer is responsible for setting an identifier
	 * on all buttons passed in through the addButton methoda after
	 * identifySComponents has been called.
	 *
	 * @param text
	 *            The text to display on the button.
	 * @param position
	 *            The position to place the button in.
	 */
	public void addButton(final String text, final int position) {
		addButton(text, text, position);
	}

	public void addButton(final String text, final String actionCommand) {
		addButton(text, actionCommand, -1);
	}

	public void addButton(final String text, final String actionCommand, final int position) {
		RadioButton but = new RadioButton(text);
		but.setActionCommand(actionCommand);
		addButton(but, position);
	}

	/**
	 * Adds a button to the next available space on the button pane. <BR>
	 * <B>Warning:</B> The developer is responsible for setting an identifier
	 * on all buttons passed in through the addButton methoda after
	 * identifySComponents has been called.
	 *
	 * @param button
	 *            The button to append at the end of this pane.
	 */
	public void addButton(final RadioButton button) {
		addButton(button, -1);
	}

	/**
	 * Add multiple buttons. <BR>
	 * <B>Warning:</B> The developer is responsible for setting an identifier
	 * on all buttons passed in through the addButton methoda after
	 * identifySComponents has been called.
	 *
	 * @param buttons
	 *            The buttons to add.
	 */
	public void addButton(final ArrayList<RadioButton> buttons) {
		for (int i = 0; i < buttons.size(); i++) {
			addButton(buttons.get(i));
		}
	}

	/**
	 * Remove the given button from the pane
	 *
	 * @param button
	 */
	public void removeButton(final RadioButton button) {
		buttonPanel.remove(button);
		buttonGroup.remove(button);
	}

	/**
	 * Gets the text string of the selected button.
	 *
	 * @return THe text displayed for the selected button.
	 */
	public String getSelectedValue() {
		ButtonModel butMod = buttonGroup.getSelection();
		if (butMod != null) {
			return butMod.getActionCommand();
		}
		return null;
	}

	/**
	 * Get the selected button.
	 *
	 * @return The selected button.
	 */
	public RadioButton getSelectedButton() {
		Enumeration buttons = buttonGroup.getElements();
		while (buttons.hasMoreElements()) {
			AbstractButton temp = (AbstractButton) buttons.nextElement();
			if (temp.isSelected()) {
				return (RadioButton) temp;
			}
		}
		return null;
	}

	/**
	 * Determine if the button with the given text is currently selected.
	 *
	 * @param text
	 *            The text of the button to determine state of.
	 * @return True if the button with the given text is selected. False
	 *         otherwise.
	 */
	public boolean getIsSelected(final String text) {
		String selected = getSelectedValue();
		if (selected != null) {
			return selected.equals(text);
		}

		return false;
	}

	public void setSelected(final String value) {
		Enumeration buttons = buttonGroup.getElements();
		while (buttons.hasMoreElements()) {
			AbstractButton button = (AbstractButton) buttons.nextElement();
			if (button.getActionCommand().equals(value)) {
				button.setSelected(true);
			}
		}
	}

	@Override
	public void setEnabled(final boolean boo) {
		Enumeration<AbstractButton> buttons = buttonGroup.getElements();
		while (buttons.hasMoreElements()) {
			AbstractButton but = buttons.nextElement();
			but.setEnabled(boo);
		}

	}

	public void clearValue() {
		Enumeration<AbstractButton> buttons = buttonGroup.getElements();
		AbstractButton current;
		while (buttons.hasMoreElements()) {
			current = buttons.nextElement();
			if (current instanceof JRadioButton) {
				current.setSelected(false);
			}
		}
	}

	/**
	 * Forwards events to appear as if it was this component which dispatched
	 * them instead of the internal components.
	 *
	 * @author Eric Lundin
	 */
	protected class GroupEventDispatacher implements ActionListener,
			ChangeListener {
		Object source;

		ArrayList<ActionListener> actionListenerList;

		ArrayList<ChangeListener> changeListenerList;

		/**
		 * Constructor.
		 */
		public GroupEventDispatacher(final Object source) {
			this.source = source;
			actionListenerList = new ArrayList<ActionListener>();
			changeListenerList = new ArrayList<ChangeListener>();
		}

		/**
		 * {@inheritDoc}
		 */
		public void stateChanged(final ChangeEvent e) {
			ChangeEvent modifiedEvent = new ChangeEvent(source);
			for (int i = 0; i < changeListenerList.size(); i++) {
				changeListenerList.get(i).stateChanged(modifiedEvent);
			}
		}

		/**
		 * {@inheritDoc}
		 */
		public void actionPerformed(final ActionEvent e) {
			ActionEvent modifiedEvent = new ActionEvent(source, e.getID(), e
					.getActionCommand(), e.getWhen(), e.getModifiers());
			for (int i = 0; i < actionListenerList.size(); i++) {
				actionListenerList.get(i).actionPerformed(modifiedEvent);
			}
		}
	}

}
